"""
Binance API 客户端
"""

from datetime import datetime
from typing import Dict, List, Optional, Literal
from binance.client import Client
from binance.exceptions import BinanceAPIException
from ..config import config


# 支持的时间周期
SUPPORTED_INTERVALS = Literal['5m', '15m', '1h', '4h', '1d']


class BinanceDataClient:
    """Binance 数据获取客户端"""

    def __init__(self) -> None:
        """初始化客户端"""
        self.api_key = config.BINANCE_API_KEY
        self.api_secret = config.BINANCE_API_SECRET
        self.base_url = config.get_binance_base_url()

        # 创建客户端实例
        try:
            # 如果提供了 API 密钥，使用认证模式
            if self.api_key and self.api_secret:
                self.client = Client(
                    api_key=self.api_key,
                    api_secret=self.api_secret,
                    testnet=config.BINANCE_TESTNET
                )
            else:
                # 使用公开 API（不需要密钥）
                self.client = Client(testnet=config.BINANCE_TESTNET)
        except Exception as e:
            raise RuntimeError(f"初始化 Binance 客户端失败: {e}")

    def get_klines(
        self,
        symbol: str,
        interval: SUPPORTED_INTERVALS,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        limit: int = 500
    ) -> List[Dict]:
        """
        获取 K 线数据

        Args:
            symbol: 交易对符号，例如 'BTCUSDT'
            interval: 时间周期 ('5m', '1h', '4h')
            start_time: 开始时间（到分钟级别）
            end_time: 结束时间（到分钟级别）
            limit: 返回数据条数，最大 1500

        Returns:
            K 线数据列表，每条数据包含：
            [
                0: 开市时间 (ms),
                1: 开盘价,
                2: 最高价,
                3: 最低价,
                4: 收盘价,
                5: 成交量,
                6: 收盘时间,
                7: 资产Quote asset volume,
                8: 交易次数,
                9: 主动买入成交量,
                10: 主动买入成交额,
                11: (忽略字段)
            ]

        Raises:
            BinanceAPIException: Binance API 调用失败时抛出
        """
        try:
            # 准备参数
            kwargs: Dict = {
                'symbol': symbol,
                'interval': interval,
                'limit': limit
            }

            # 添加时间参数
            if start_time:
                kwargs['startTime'] = int(start_time.timestamp() * 1000)
            if end_time:
                kwargs['endTime'] = int(end_time.timestamp() * 1000)

            # 获取数据
            klines = self.client.get_klines(**kwargs)

            # 转换为字典格式
            result = []
            for kline in klines:
                result.append({
                    'open_time': kline[0],
                    'open': float(kline[1]),
                    'high': float(kline[2]),
                    'low': float(kline[3]),
                    'close': float(kline[4]),
                    'volume': float(kline[5]),
                    'close_time': kline[6],
                    'quote_asset_volume': float(kline[7]),
                    'number_of_trades': int(kline[8]),
                    'taker_buy_base_asset_volume': float(kline[9]),
                    'taker_buy_quote_asset_volume': float(kline[10]),
                })

            return result

        except BinanceAPIException as e:
            raise BinanceAPIException(
                f"Binance API 错误: {e.status_code} - {e.message}",
                e.status_code
            )
        except Exception as e:
            raise RuntimeError(f"获取 K 线数据失败: {e}")

    def validate_symbol(self, symbol: str) -> bool:
        """
        验证交易对是否存在

        Args:
            symbol: 交易对符号

        Returns:
            是否存在
        """
        try:
            # 获取交易规则
            exchange_info = self.client.get_exchange_info()
            symbols = {item['symbol'] for item in exchange_info['symbols']}
            return symbol in symbols
        except Exception as e:
            raise RuntimeError(f"验证交易对失败: {e}")

    def get_open_interest(self, symbol: str) -> Dict[str, float]:
        """
        获取 Open Interest（未平仓合约价值）

        Args:
            symbol: 交易对符号，如 'BTCUSDT'

        Returns:
            包含 'openInterest' 的字典

        Raises:
            RuntimeError: 获取 Open Interest 失败时抛出
        """
        try:
            # 使用期货 API 获取 Open Interest
            oi_data = self.client.futures_open_interest(symbol=symbol)
            return {
                'open_interest': float(oi_data['openInterest'])
            }
        except BinanceAPIException as e:
            raise RuntimeError(f"获取 Open Interest 失败: {e.message}")
        except Exception as e:
            raise RuntimeError(f"获取 Open Interest 失败: {e}")

    def get_funding_rate(self, symbol: str) -> Dict[str, float]:
        """
        获取 Funding Rate（资金费率）

        Args:
            symbol: 交易对符号，如 'BTCUSDT'

        Returns:
            包含 'funding_rate' 的字典

        Raises:
            RuntimeError: 获取 Funding Rate 失败时抛出
        """
        try:
            # 使用期货 API 获取 Premium Index（包含 Funding Rate）
            premium_data = self.client.futures_mark_price(symbol=symbol)
            return {
                'funding_rate': float(premium_data['lastFundingRate'])
            }
        except BinanceAPIException as e:
            raise RuntimeError(f"获取 Funding Rate 失败: {e.message}")
        except Exception as e:
            raise RuntimeError(f"获取 Funding Rate 失败: {e}")

    def get_futures_data(self, symbol: str) -> Dict[str, float]:
        """
        获取期货相关数据（Open Interest 和 Funding Rate）

        Args:
            symbol: 交易对符号，如 'BTCUSDT'

        Returns:
            包含 Open Interest 和 Funding Rate 的字典

        Raises:
            RuntimeError: 获取期货数据失败时抛出
        """
        try:
            # 获取 Open Interest
            oi_data = self.get_open_interest(symbol)
            # 获取 Funding Rate
            fr_data = self.get_funding_rate(symbol)
            # 合并结果
            return {
                'open_interest': oi_data['open_interest'],
                'funding_rate': fr_data['funding_rate']
            }
        except Exception as e:
            # 如果期货数据获取失败，返回默认值
            return {
                'open_interest': 0.0,
                'funding_rate': 0.0
            }
